﻿using System.Net.Sockets;
using System;
using System.Net;
using System.IO;
using System.Threading;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Reflection;

namespace orb
{
    public class Server: IDisposable
    {
        Dictionary<string, Icon> icons = new Dictionary<string, Icon>();
        Dictionary<string, NotifyIcon> monitors = new Dictionary<string, NotifyIcon>();

        HttpListener listener = new HttpListener();
        TcpListener serverSocket = new TcpListener(IPAddress.Any, 4242);
        ThreadStart threadStart;
        Thread thread;

        GuiRunner GuiRunner;

        public Server(GuiRunner guiRunner)
        {
            GuiRunner = guiRunner;

            foreach(var colour in new[]{"green", "yellow", "red"})
                icons[colour] = GetIcon(colour);

            threadStart = new ThreadStart(Serve);
            thread = new Thread(threadStart);
            thread.Start();
        }

        static Icon GetIcon(string name)
        {
            var resourceName = string.Format("orb.icons.{0}.ico", name);
            var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
            var result = new Icon(stream);
            return result;
        }

        void StoreTrayIcon(string app)
        {
            // TODO: Receive GUI events without Form
            GuiRunner.RunOnGuiThread(
                () =>
                {
                    var trayIcon = new NotifyIcon();
                    trayIcon.Text = app;
                    trayIcon.Visible = true;
                    trayIcon.DoubleClick += CloseTrayIcon;
                    monitors[app] = trayIcon;
                }
            );
        }

        void Serve()
        {
            serverSocket.Start();
            while (true)
            {
                using (var client = serverSocket.AcceptTcpClient())
                using (var stream = client.GetStream())
                {
                    var sr = new StreamReader(stream);
                    for (;;)
                    {
                        var line = sr.ReadLine();
                        if (line == null || line.Length==0) break;

                        var r = new Regex("^GET /([^/]+)/(.+) HTTP/1.[01]$");
                        var m = r.Match(line);
                        if (m.Success)
                        {
                            var app = m.Groups[2].Value;
                            var colour = m.Groups[1].Value;

                            if (!monitors.ContainsKey(app))
                            {
                                StoreTrayIcon(app);
                            }
                            else if (monitors[app].Icon != icons[colour])
                            {
                                monitors[app].ShowBalloonTip(
                                    1000, 
                                    app, 
                                    string.Format(
                                        "Changed to {0}",
                                        colour
                                    ),
                                    ToolTipIcon.None
                                );
                            }

                            monitors[app].Icon = icons[colour];
                        }
                    }

                    using (var sw = new StreamWriter(stream))
                    {
                        sw.WriteLine("HTTP/1.1 200 OK");
                        sw.WriteLine();
                    }
                }
            }
        }

        void CloseTrayIcon(object sender, EventArgs ev)
        {
            using (var icon = (NotifyIcon)sender)
            {
                var toRemove = new List<string>();

                lock (monitors)
                {
                    foreach (var key in monitors.Keys)
                    {
                        var value = monitors[key];
                        if (value == icon)
                        {
                            toRemove.Add(key);
                        }
                    }

                    foreach (var key in toRemove)
                        monitors.Remove(key);
                }
            }
        }

        public void Dispose()
        {
            thread.Abort();
            foreach(var trayIcon in monitors.Values)
                using (trayIcon) { }
        }
    }
}
